/*
    Author: Christopher Diggins
    License: MIT Licence 1.0
    Website: http://www.cdiggins.com
*/

#define F2R1(INTERFACE, FUNCTION) { a.GetInterface<INTERFACE>().FUNCTION(At(1), At(0)); Pop(); } 
#define F1R1(INTERFACE, FUNCTION) { a.GetInterface<INTERFACE>().FUNCTION(At(0)); } 

namespace cvm
{
	struct StaticSubType : ValType<IFunction>
	{		
		PChar TypeName() { return "StaticSub"; } 
		int ToInt(Any& a) { return a.data.n; }
		void Write(Any& x, Any& y) { fprintf(y.ToFile(), "sub%d", x.ToInt()); } 
		void Apply(Any& a, VM& vm);
	};
	StaticSubType gStaticSubType;

	struct QuotedValueType : RefType<IFunction, Any>
	{
		PChar TypeName() { return "QuotedValue"; }
		void Write(Any& x, Any& y) { 
			FILE* f = y.ToFile();
			fprintf(f, "["); 
			GetObject(x).Write(f); 
			fprintf(f, "]"); 
		}
		void Apply(Any& a, VM& vm) {
			VM.Push(GetObject(a));
		}
		void Finalize(Any& a) {
			GetObject(a).DecRef();
		}
	};
	QuotedValueType gQuotedValueType;

	struct QuotedValue : RefType<IFunction, Any>
	{
		PChar TypeName() { return "QuotedValue"; }
		void Write(Any& x, Any& y) { 
			FILE* f = y.ToFile();
			fprintf(f, "["); 
			GetObject(x).Write(f); 
			fprintf(f, "]"); 
		}
		void Apply(Any& a, VM& vm) {
			VM.Push(GetObject(a));
		}
		void Finalize(Any& a) {
			GetObject(a).DecRef();
		}
	};

	struct VM : AnyStack
	{
		uchar* code;
		uchar* ic;

		AnyStack aux; 

		char* data;
		int data_size;
		Any top;	
		Any tmp;
		
		char* constant_pool;
		int num_constants;
		char** constant_indexes;
		int num_functions;
		int* function_indexes;

		VM() 
		{
			sp = stack;
			ap = aux;
			top = Any::undefined();
		}

		void LoadConstants(char* pool, int pool_size) {
			constants 
		}

		void PushFrom(Any& x) {
			stk.
			PushNoAny();
			Move(x, top);
		}

		void ToAux() {
			MoveTopTo(aux);
		}

		void FromAux() {
			aux.MoveTopTo(*this);
		}

		Any* AuxStackLimit() {
			return aux + AUX_STACK_SIZE;
		}

		Any* StackLimit() {
			return stack + STACK_SIZE;
		}

		void PushNoAny() {
			assert(sp >= stack);
			assert(sp < StackLimit());
			Move(top, *sp++);
		}

		void PushFrom(Val* val) {
			PushNoAny();
			top.Copy(*val);
		}

		void Pop() {
			top.Destroy(*this);
			PopNoDestroy();
		}
	
		void Pull(Val& x) {
			Move(top, x);
			Move(*--sp, top);
		}

		template<typename T>
		void PullAs(T*& x) {
			Val tmp;
			Pull(tmp);
			x = tmp.To<T>();
		}

		void PopNoDestroy() {
			Move(*--sp, top);
		}

		void Call(int n) {
			// TODO:	
		}

		template<typename T, int N>
		void PushConstant() {			
			Push(GetConstant<T, N>());
		}

		template<typename T, int N>
		void GetConstant() {			
			T r;
			char* buf = reinterpret_cast<char*>(&r);
			for (int i=0; i < N; ++i) 
				buf[i] = *ic++;
			return r;
		}

		void PopN(int n) {
			for (int i=0; i < n; ++i) 
				Pop();
		}

		void StoreIC() {
			aux.Push(ic);
		}

		void RestoreIC() {
			ic = aux.Top().ToInt();
			aux.Pop();
		}

		void Apply() {
			// Get the top function
		}

		void TerminateCurrent() {
			Val current_function;
			Move(*--ap, current_function);
			current_function.DecRef();
		}

		void TailApply() {
			TerminateCurrent();
			ToAux();
		}

		void Ret() {
			TerminateCurrent();
			RestoreIC();
		}

		void Quote();

		void Dip() {
			Swap();
			Quote();
			Compose();
			Apply();
		}

		char* GetString(int n) {
			constant_pool + constant_indexes[n];
		}

		template<typename F>
		void BinaryFunction(F& f) {			
			// TODO: check for underflow, etc.
			Move(f(top, *--sp), top);
		}

		void Dispatch();
	}

	struct Subroutine : IFunction {
		Subroutine(int n) {
		}

		void Apply(VM& vm) {
			vm.ic = vm.GetFunction(To<uchar*>());
		}
	};

	struct QuotedAny : Reference<IFunction, Any>
	{
		QuotedAny(Val& v) 
			: Reference(v)
		{
		}
		
		~QuotedAny() {			
		}

		void Apply(VM& vm) {
			vm.Push(GetObject());
		}
	};

	struct DippedFunction_Impl 
	{
		IFunction fun;
		Val val;
	};

	struct DippedFunction : Reference<IFunction, DippedFunction> 
	{
		DippedFunction(Fun& f, Val& v)
	};

	void VM::Quote() {
		QuotedAny qv(top);
		top.CopyFrom(&qv);
	}

	void VM::Dip() {
		DippedFunction cf(top.As<IFunction>(), *--sp);
		cf.App
	}
}
